/**
 * PHOSIDE: PHosphorylation Site IDentification Engine.
 * Copyright 2009 Digital Biology Lab, University of Missouri.
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version. <p/> This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more
 * details.
 */

package phoside.miscellaneous.test;

import java.io.BufferedWriter;
import java.io.FileWriter;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import phoside.PhosphoProteins;
import phoside.PhosphoProteinsImpl;

import phoside.data.feature.extaction.DisorderFeatureExtractor;
import phoside.data.feature.extaction.AminoAcidFrequencyFeatureExtractor;
import phoside.data.feature.extaction.InstancesExtractorFromProteins;
import phoside.data.feature.extaction.InstancesExtractor;
import phoside.data.feature.extaction.KNNFeatureExtractor;
import phoside.data.feature.model.Instance;

import phoside.io.xml.PhosideXmlProteinsReader;
import phoside.io.xml.PhosphoProteinFieldValueFormatter;

import phoside.knn.ClosenessCalculator;
import phoside.knn.KNNExtractor;
import phoside.knn.KNNExtractorImpl;
import phoside.knn.SequenceBasedClosenessCalculator;
import phoside.knn.matrix.similarity.MatrixNormalizer;
import phoside.knn.matrix.similarity.MatrixNormalizerLinear;
import phoside.knn.matrix.similarity.MatrixUtils;
import phoside.knn.matrix.similarity.SimilarityMatrix;

import phoside.util.CrossValidation;
import phoside.util.IOUtil;
import phoside.util.Pair;

/**
 *
 * @author gjj
 */
public class FeatureEvaluation {


    public static void main(String[] args) {
//        String xmlhuman = "data\\phosphoelm\\Human-UniProt-SwissProt-V15.8-20090922-PhosphoELM-V8.2-April2009-nr50.xml";
//        String xmlmouse = "data\\phosphoelm\\Mouse-SwissProt-V57.8-20090922-PhosphoELM-V8.2-April2009-nr50.xml";
//        String xmlfly = "data\\phosphopep\\FluitFly-UniProt-SwissProt-V15.8-20090922-PhophoPep-20091002-nr50.xml";
//        String xmlworm = "data\\phosphopep\\Worm-UniProt-SwissProt-V15.8-20090922-PhophoPep-20091002-nr50.xml";
//        String xmlyeast = "data\\uniprot\\Yeast-UniProt-Complete-Proteome-SwissProt-phospho-V15.8-20090922-nr50.xml";
//        String xmlara = "data\\phosphat\\Arabidopsis-TAIR9-PhosPhAt3-nonambiguous-with-disorder-nr50.xml";
//        String dirOuthuman = "exp\\feature-evl\\human-";
//        String dirOutmouse = "exp\\feature-evl\\mouse-";
//        String dirOutfly = "exp\\feature-evl\\fly-";
//        String dirOutworm = "exp\\feature-evl\\worm-";
//        String dirOutyeast = "exp\\feature-evl\\yeast-";
//        String dirOutara = "exp\\feature-evl\\arabidopsis-";
//        String dirOuthumantyr = "exp\\feature-evl\\human-tyr-";
//        String dirOutmousetyr = "exp\\feature-evl\\mouse-tyr-";
//
//
        Set<PhosphoProteins.PhosphoType> types = new HashSet();
        types.add(PhosphoProteins.PhosphoType.SER);
        types.add(PhosphoProteins.PhosphoType.THR);

//        main1(xmlhuman, dirOuthuman, types);
//        main1(xmlmouse, dirOutmouse, types);
//        main1(xmlfly, dirOutfly, types);
//        main1(xmlworm, dirOutworm, types);
//        main1(xmlyeast, dirOutyeast, types);
//        main1(xmlara, dirOutara, types);
        
//        types.clear();
//        types.add(PhosphoProteins.PhosphoType.TYR);
//        main1(xmlhuman, dirOuthumantyr, types);
//        main1(xmlmouse, dirOutmousetyr, types);

        String xml = "D:\\projects\\p3db\\data\\Medicago-Grimsrud-with-disorder.xml";
        String dirOut = "D:\\projects\\p3db\\data\\Medicago-Grimsrud.disorder.feature";
        main1(xml,dirOut,types);

    }

    public static void main1(String xml, String dirOut, Set<PhosphoProteins.PhosphoType> types) {

        PhosphoProteins proteins = new PhosphoProteinsImpl();
        PhosideXmlProteinsReader reader = new PhosideXmlProteinsReader(xml, proteins, new PhosphoProteinFieldValueFormatter());
        try {
            reader.read();
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        main2(proteins, dirOut, types);
    }
    
    public static void main2(PhosphoProteins proteins, String dirOut, Set<PhosphoProteins.PhosphoType> types) {

        int offset = 6;

        System.out.println("Extracting positive instance...");
        InstancesExtractor insExtractor = new InstancesExtractorFromProteins(proteins, null, types, offset, InstancesExtractor.PhosphoTag.PHOSPHO);
        List<Instance> instances_positive = insExtractor.fetch(-1);
        System.out.println("  "+instances_positive.size()+" instances were extracted");
        System.out.println("Extracting unlabeled instance...");
        //double extractRatio = proteins.proteinCount()<=10000?1:10000.0/proteins.proteinCount(); // to save memory
        insExtractor = new InstancesExtractorFromProteins(proteins, null, types, offset, InstancesExtractor.PhosphoTag.NONPHOSPHO);
        List<Instance> instances_unlabeled = insExtractor.fetch(-1);
        System.out.println("  "+instances_unlabeled.size()+" instances were extracted");

        // knn
//        double[] kps = new double[]{0.005,0.01,0.02,0.04,0.08};
//        knnFeatures(instances_positive, instances_unlabeled, types, offset, kps,
//                dirOut+"knn-pos.txt", dirOut+"knn-neg.txt");

        //List<Instance> instances_unlabeled = null;
        disorderFeatures(instances_positive, instances_unlabeled, types, offset,
                dirOut+"dis-pos.txt", dirOut+"dis-neg.txt");

//        frequencyFeatures(instances_positive, instances_unlabeled, types, offset,
//                dirOut+"fre-pos.txt", dirOut+"fre-neg.txt");
    }

    public static void disorderFeatures(List<Instance> instances_positive,
            List<Instance> instances_unlabeled, Set<PhosphoProteins.PhosphoType> types,
            int offset, String dirPos, String dirNeg) {
        int[] disWindowOffsets = new int[2*offset+1];
        for (int i=0; i<2*offset+1; i++ ) {
            disWindowOffsets[i] = i-offset;
        }
        DisorderFeatureExtractor disorderExtractor = new DisorderFeatureExtractor(disWindowOffsets, false);

        try {
            //List<String> outPos = new ArrayList();
            FileWriter writer = new FileWriter(dirPos);
            BufferedWriter out = new BufferedWriter(writer);
            for (Instance ins : instances_positive) {
                List diss = disorderExtractor.extract(ins, false);
                if (diss==null) continue;
                out.write(StringUtils.join(diss,'\t'));
                out.newLine();
            }
            out.close();
            writer.close();

            if (instances_unlabeled!=null) {
                writer = new FileWriter(dirNeg);
                out = new BufferedWriter(writer);
                for (Instance ins : instances_unlabeled) {
                    List diss = disorderExtractor.extract(ins, false);
                    if (diss==null) continue;
                    out.write(StringUtils.join(diss,'\t'));
                    out.newLine();
                }
                out.close();
                writer.close();
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void frequencyFeatures(List<Instance> instances_positive,
            List<Instance> instances_unlabeled, Set<PhosphoProteins.PhosphoType> types, 
            int offset, String dirPos, String dirNeg) {
        AminoAcidFrequencyFeatureExtractor freExtractor = new AminoAcidFrequencyFeatureExtractor(offset);

        try {
            //List<String> outPos = new ArrayList();
            FileWriter writer = new FileWriter(dirPos);
            BufferedWriter out = new BufferedWriter(writer);
            for (Instance ins : instances_positive) {
                out.write(StringUtils.join(freExtractor.extract(ins, false),'\t'));
                out.newLine();
            }
            out.close();
            writer.close();

            writer = new FileWriter(dirNeg);
            out = new BufferedWriter(writer);
            for (Instance ins : instances_unlabeled) {
                out.write(StringUtils.join(freExtractor.extract(ins, false),'\t'));
                out.newLine();
            }
            out.close();
            writer.close();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void knnFeatures(List<Instance> instances_positive,
            List<Instance> instances_unlabeled, Set<PhosphoProteins.PhosphoType> types, 
            int offset, double[] kps, String dirPos, String dirNeg) {
        
        // get training and test instances
        int nTest = 1000;
        Pair<List,List> ptt = CrossValidation.seperateTrainingTestSet(instances_positive, nTest);
        List<Instance> positive_train = ptt.getFirst();
        int nTrain = positive_train.size();
        List<Instance> positive_test = ptt.getSecond();

        ptt = CrossValidation.seperateTrainingTestSet(instances_unlabeled, nTest);
        List<Instance> negative_train = CrossValidation.seperateTrainingTestSet(ptt.getFirst(),nTrain).getSecond();
        List<Instance> negative_test = ptt.getSecond();

        List<Instance> instances_train = new ArrayList<Instance>(positive_train);
        instances_train.addAll(negative_train);

        List<Double> labels_train = new ArrayList(nTrain+nTrain);
        for (int i=0; i<nTrain; i++) {
            labels_train.add(1.0);
        }
        for (int i=0; i<nTrain; i++) {
            labels_train.add(-1.0);
        }

        // ks
        int[] ks = new int[kps.length];
        for (int i=0; i<kps.length; i++) {
            ks[i] = (int) (nTrain * kps[i]);
        }

        // extractor
        String dirMatrix = "resource/matrix/blosum62";
        SimilarityMatrix similarityMatrix;
        try {
            similarityMatrix = MatrixUtils.readBLASTMatrix(dirMatrix);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        
        MatrixNormalizer matrixNormalizer = new MatrixNormalizerLinear();
        MatrixUtils.normalizeMatrix(similarityMatrix, matrixNormalizer);
        ClosenessCalculator closenessCalculator = new SequenceBasedClosenessCalculator(similarityMatrix, offset);
        KNNExtractor nnExtractor = new KNNExtractorImpl(closenessCalculator);
        KNNFeatureExtractor knnExtractor = new KNNFeatureExtractor(ks, nnExtractor);

        knnExtractor.setTrainingData(instances_train, labels_train);

        List<String> outPos = new ArrayList();
        for (Instance ins : positive_test) {
            outPos.add(StringUtils.join(knnExtractor.extract(ins, false),'\t'));
        }

        List<String> outNeg = new ArrayList();
        for (Instance ins : negative_test) {
            outNeg.add(StringUtils.join(knnExtractor.extract(ins, false),'\t'));
        }

        try {
            IOUtil.writeCollectionAscii(outPos, dirPos);
            IOUtil.writeCollectionAscii(outNeg, dirNeg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
